<?php
/**
* Lớp mở rộng hỗ trợ Upload file
* @author Mr.UBKey
*/
class XPHP_Upload
{ 
    //Instance to keep Uploader object.
    private static $instance;
    
    //Private properties of the class, change them with the setters methods bellow.
    private $uploadPath = 'data/temp/';
    private $newName =false;
    private $allowOverwrite = false;
    private $newExtension =false;
    private $encryptName = false;
    private $maxSize = 2000;
    private $maxHeight = 2000;
    private $maxWidth = 2000;
    private $fileType = 'image';
    private $error = false;
    private $acceptExt = false;
    private $fileExt = "";
    
    //No direct instantiating or cloning of the object. We need only one.
    public function __construct(){}
    
    private function __clone(){}
    
    //Singleton method to fill up the instance.
    public static function getSingleton()
    {
        if(!self::$instance OR is_null(self::$instance))
        {
            self::$instance = new XPHP_Upload();
        }
        return self::$instance;
    }
    
    /**
     * Phương thức thực hiện upload file
     * Trả về mảng chứa đường dẫn , tên file, đuôi mở rộng của file
     * @param $formFile Tên của input file
     * @return array
     */
    public function doUpload($formFile)
    {
        if ( ! $this->validate_upload_path())
        {
            // errors will already be set by validate_upload_path() so just return FALSE
            return FALSE;
        }
        //No file to upload.
        if(!isset($_FILES[$formFile]) OR $_FILES[$formFile]['error'] != 0)
        {
            $this->setError('Bạn chưa chọn file để upload');
        }
        //Check if it's uploaded file.
        if(!is_uploaded_file($_FILES[$formFile]['tmp_name']))
        {
            $this->_errorDelete($formFile);
            $this->setError('File upload chưa thành công');
        }
        //Get the extension or false.
        $this->fileExt = $this->_isCorrectFile($_FILES[$formFile]['type'], $this->fileType, $formFile);
        if(!$this->fileExt)
        {
            $this->_errorDelete($formFile);
            $this->setError('Định dạng file ' . $this->fileExt . ' không được phép tải lên ');
        }
        
        //Check for any upload errors.
        if($_FILES[$formFile]['error'] != 0)
        {
            $this->_errorDelete($formFile);
            $this->setError('Có lỗi trong khi upload file. Vui lòng thử lại');
        }
        
        //Check for the maximum size of the file.
        if($_FILES[$formFile]['size'] > $this->maxSize * 1024)
        {
            $this->_errorDelete($formFile);
            $this->setError('Kích thước file không được vượt quá '. $this->maxSize .' kb');
        }
        
        //If we have image. Check for maximum width and height.
        if($this->_isExtImg($formFile))
        {
            $dimension = @getimagesize($_FILES[$formFile]['tmp_name']);
            if($dimension[0] > $this->maxWidth)
            {
                $this->_errorDelete($formFile);
                $this->setError('Chiều dài file ảnh vượt quá kích thước cho phép tối đa là' .$this->maxWidth .' px');
            }
            if($dimension[1] > $this->maxHeight)
            {
                $this->_errorDelete($formFile);
                $this->setError('Chiều cao file ảnh vượt quá kích thước cho phép tối đa là' . $this->maxHeight . ' px');
            }
        }
        
        //Clean the name and check if it's empty.
        if(!$this->newName){
            $this->newName = $this->_cleanFileName($_FILES[$formFile]['name']);
            if($_FILES[$formFile]['name'] == ''){
                $this->_errorDelete($formFile);
                $this->setError('Tên file không được chứa các ký tự đặc biệt');
            }
            
            if ($this->allowOverwrite == FALSE){
                $this->newName = $this->setFilename($this->uploadPath, $this->newName);
                
                if ($this->newName === FALSE)
                {
                    return FALSE;
                }
            }
        }
        //Try to copy or move the uploaded file. This is from the CI upload class.
        if(!@copy($_FILES[$formFile]['tmp_name'], $this->uploadPath.$this->newName))
        {
            if(!@move_uploaded_file($_FILES[$formFile]['tmp_name'], $this->uploadPath.$this->newName))
            {
                $this->_errorDelete($formFile);
                $this->setError('Không thể upload được file');
            }
        }
        
        //Return the path and the new name with the extension... Change it to whatever you want.
        if($this->error !== false) return array('error'=>$this->error);
        return array('path'=>$this->uploadPath,'name'=>$this->newName,'ext'=>$this->fileExt);
    }
    
    /**
     * Gán đường dẫn tới file cần upload
     * @param string $path
     * @return MFX_Upload
     */
    public function setPath($path)
    {
        $this->uploadPath = rtrim($path, '/').'/';  
        
        return $this;
    }
    
    /**
     * Gán tên mới cho file
     * @param string $name
     * @return MFX_Upload
     */
    public function setNewName($name)
    {
        $this->newName = $name;
        
        return $this;
    }
    
    /**
     * Gán dung lượng tối đa của file
     * @param int $size
     * @return MFX_Upload
     */
    public function setMaxSize($size)
    {
        $this->maxSize = $size;
        
        return $this;
    }
    
    /**
     * Gán độ rộng tối đa của file
     * @param unknown_type $width
     * @return MFX_Upload
     */
    public function setMaxWidth($width)
    {
        $this->maxWidth = $width;
        
        return $this;
    }
    
    /**
     * Gán độ cao tối đa của file
     * @param unknown_type $height
     * @return MFX_Upload
     */
    public function setMaxHeight($height)
    {
        $this->maxHeight = $height;
        
        return $this;
    }
    
    /**
     * Gán phần mở rộng của file
     * @param $type
     * @param $acceptExt
     * @return MFX_Upload
     */
    public function setFileType($type,$acceptExt = false)
    {
        $this->fileType = $type;
        $this->acceptExt =  $acceptExt;
        
        return $this;
    }
    
    /**
     * Lấy ra phần mở rộng của file
     * @return string
     */
    public function getFileExt($formFile)
    {
    	$type = $this->_isCorrectFile($_FILES[$formFile]['type'], $this->fileType, $formFile);
    	return $type;
    }
	
    /**
     * Gán lỗi
     * @param $string
     * @return MFX_Upload
     */
    private function setError($string){
        $this->error[] = $string;
    }
    
    //Private methods.
    
    //Try to delete the file without throwing error.
    private function _errorDelete($formFile)
    {
        @unlink($_FILES[$formFile]['tmp_name']);
    }
    
    /**
     * Kiểm tra tính chính xác của file
     * @param $file Tên file
     * @param $type Kiểu file
     * @param $formFile
     */
    private function _isCorrectFile($file, $type = 'image', $formFile)
    {
        switch($type)
        {
            case 'image':
                return $this->_isImage($file);
            break;
            case 'xml':
                return $this->_isXml($formFile);
            break;
            case 'custom':
                return $this->_isCustom($formFile);
                break ;
                
        }
    }
    
    /**
     * Kiểm tra xem file có phải là file xml hay không ?
     * @param $formFile
     * @return xml
     */
    private function _isXml($formFile)
    {
        $extension = strtolower(end(explode('.', $_FILES[$formFile]['name'])));   
        if($extension == 'xml')
        {
            return 'xml';
        }
        return false;
    }
    
    /**
     * Kiểm tra xem tệp tin có phải là tệp tin ảnh
     * @param $file Tên tệp tin
     * @return boolean 
     */
    private function _isImage($file)
    {
        $pngMimes  = array('image/x-png', 'image/png');
        $jpegMimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg');
        $gifMimes = array('image/gif');
        if(in_array($file, $pngMimes))
        {
            $file = 'png';
        }
        if(in_array($file, $jpegMimes))
        {
            $file = 'jpeg';
        }
        if(in_array($file, $gifMimes))
        {
            $file = 'gif';
        }
        $imgMimes = array(
            'gif',
            'jpeg',
            'png',
        );
        return (in_array($file, $imgMimes)) ? $file : FALSE;
    }
    
    /**
     * Kiểm tra xem có phải file do người dùng tự định nghĩa
     * @param $formFile
     * @return boolean 
     */
    private function _isCustom($formFile) 
    {
        $extension = strtolower(end(explode('.', $_FILES[$formFile]['name'])));  
        return (in_array($extension, $this->acceptExt)) ? $extension : FALSE;
    }
    
    /**
     * Kiểm tra phần mở rộng có phải là phần mở rộng của ảnh
     * @param $ext Đuôi mở rộng
     * @return boolean 
     */
    private function _isExtImg($ext)
    {
        $imgs = array('png', 'jpeg', 'gif');
        if(in_array($ext, $imgs))
        {
            return true;
        }
        return false;
    }
    
    
    /**
     * Xóa các kí tự đặc biệt từ tên file
     * @param $filename Tên file
     * @return $filename Tên file đã được xử lý
     */
    private function _cleanFileName($filename)
    {
        $bad = array(
                        "<!--",
                        "-->",
                        "'",
                        " ",
                        "<",
                        ">",
                        '"',
                        '&',
                        '$',
                        '=',
                        ';',
                        '?',
                        '/',
                        "%20",
                        "%22",
                        "%3c",        // <
                        "%253c",     // <
                        "%3e",         // >
                        "%0e",         // >
                        "%28",         // (
                        "%29",         // )
                        "%2528",     // (
                        "%26",         // &
                        "%24",         // $
                        "%3f",         // ?
                        "%3b",         // ;
                        "%3d"        // =
                    );
                    
        $filename = str_replace($bad, '', $filename);
        return stripslashes($filename);
    }
    
    /**
     * Gán tên file
     * @param $path Đường dẫn đến file
     * @param $filename Tên file
     */
    function setFilename($path, $filename)
    {
        if ($this->encryptName == TRUE)
        {        
            mt_srand();
            $filename = md5(uniqid(mt_rand())).$this->fileExt;    
        }
    
        if ( ! file_exists($path.$filename))
        {
            return $filename;
        }
    
        $filename = str_replace($this->fileExt, '', $filename);
        
        $new_filename = '';
        for ($i = 1; $i < 100; $i++)
        {            
            if ( ! file_exists($path.$filename.$i.'.'.$this->fileExt))
            {
                $new_filename = $filename.$i.'.'.$this->fileExt;
                break;
            }
        }

        if ($new_filename == '')
        {
            $this->set_error('File đã tồn tại');
            return FALSE;
        }
        else
        {
            return $new_filename;
        }
    }
    
    
    /**
     * Kiểm tra đường dẫn của thư mục upload tới
     * @return    bool
     */    
    function validate_upload_path()
    {
        if ($this->uploadPath == '')
        {
            $this->setError('Không tồn tại đường dẫn thư mục upload');
            return FALSE;
        }
        
        if (function_exists('realpath') AND @realpath($this->uploadPath) !== FALSE)
        {
            $this->uploadPath = str_replace("\\", "/", realpath($this->uploadPath));
        }

        if ( ! @is_dir($this->uploadPath))
        {
            $this->setError('Không tồn tại đường dẫn thư mục upload');
            return FALSE;
        }

//        if ( ! is_really_writable($this->uploadPath))
//        {
//            $this->setError('Thư mục upload không có quyền ghi');
//            return FALSE;
//        }

        $this->uploadPath = preg_replace('/(.+?)\/*$/', '\\1/',  $this->uploadPath);
        return TRUE;
    }


}